unit database_tilesets;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  EditBtn, database_base, itemselectorframeunit, tilepaletteunit, fpjson,
  tilesetgraphicsunit, imageselection;

type

  { TDatabaseTilesetsFrame }

  TDatabaseTilesetsFrame = class(TDatabaseBaseFrame)
    A1EditButton: TEditButton;
    A2EditButton: TEditButton;
    A3EditButton: TEditButton;
    A2Label: TLabel;
    A4EditButton: TEditButton;
    A3Label: TLabel;
    A2Panel: TPanel;
    A5EditButton: TEditButton;
    A4Label: TLabel;
    A3Panel: TPanel;
    BEditButton: TEditButton;
    A5Label: TLabel;
    A4Panel: TPanel;
    CounterRadioButton: TRadioButton;
    CEditButton: TEditButton;
    BLabel: TLabel;
    A5Panel: TPanel;
    DamageRadioButton: TRadioButton;
    ImpassableByBoatRadioButton: TRadioButton;
    DEditButton: TEditButton;
    CLabel: TLabel;
    BPanel: TPanel;
    EEditButton: TEditButton;
    DLabel: TLabel;
    CPanel: TPanel;
    ELabel: TLabel;
    DPanel: TPanel;
    EPanel: TPanel;
    BitSelectorGroupBox: TGroupBox;
    ImagesGroupBox: TGroupBox;
    A1Label: TLabel;
    BushRadioButton: TRadioButton;
    AirsipCantLandRadioButton: TRadioButton;
    NotesMemo: TMemo;
    ModeComboBox: TComboBox;
    ContentPanel: TPanel;
    ModeLabel: TLabel;
    NameEdit: TEdit;
    GeneralSettingsGroupBox: TGroupBox;
    NameLabel: TLabel;
    MainScrollBox: TScrollBox;
    A1Panel: TPanel;
    AdditionalPanel: TPanel;
    BitSelectorScrollBox: TScrollBox;
    PassabilityRadioButton: TRadioButton;
    FourDirectionRadioButton: TRadioButton;
    LadderRadioButton: TRadioButton;
    ImpassableByShipRadioButton: TRadioButton;
    TerrainTagRadioButton: TRadioButton;
    SelectorContentSplitter: TSplitter;
    MainTilesSplitter: TSplitter;
    BitSelectorNotesSplitter: TSplitter;
    TilesAdditionalSplitter: TSplitter;
    TilesetTilePaletteFrame: TTilePaletteFrame;
    TilesetSelectorFrame: TItemSelectorFrame;
    procedure FourDirectionRadioButtonChange(Sender: TObject);
    procedure Init; override;
    procedure ModeComboBoxChange(Sender: TObject);
    procedure NameEditChange(Sender: TObject);
    procedure NotesMemoChange(Sender: TObject);
    procedure PassabilityRadioButtonChange(Sender: TObject);
    procedure SingleBitRadioButtonChange(Sender: TObject);
    procedure LoadTilesetData(NewId: Integer);
    function GetSelectedTileset: TJSONObject;
    procedure TerrainTagRadioButtonChange(Sender: TObject);
    procedure TilesetEditButtonClick(Sender: TObject);
    procedure UpdateTilesetGraphics(Ts: TJSONObject);
    procedure LoadTilesetNames(Names: TJSONArray);
  private
    CurrentId: Integer;
    IsLoading: Boolean;
    TilesetGraphics: TTilesetGraphics;
    TilesetFlags: TJSONArray;

    function GetEditedTileSetting: TEditedTileSetting;
    function GetEditedBit: Integer;
  public
    destructor Destroy; override;
  end;

resourcestring
  TilesetsHeader = 'Tilesets';

  {
  #0  1 = нет хода вниз
  #1  2 = нет хода влево
  #2  4 = нет хода вправо
  #3  8 = нет хода вверх
  #4  16 = «звёздочка» (находится ли тайл над героем/героиней)
  #5  32 = лестница (героиня смотри вверх)
  #6  64 = кусты (герой частично полупрозрачен)
  #7  128 = стол (можно ли активировать ивенты между героиней и этим тайлом)
  #8  256 = повреждение при ходьбе
  #9  512 = непроходимость для лодки
  #10  1024 = непроходимость для корабля
  #11 2048 = невозможность приземлиться на дирижабле/шаре
  в битах выше хранится тег земли, (n*4096) = terrain tag n (1..7)
}

implementation

{$R *.lfm}

uses
  comboboxhelper;

{ TDatabaseTilesetsFrame }

procedure TDatabaseTilesetsFrame.Init;
begin
  IsLoading := True;
  TilesetSelectorFrame.SetData(Db.Tilesets, @Db.ResizeTilesets, @LoadTilesetData);
  TilesetSelectorFrame.Title := TilesetsHeader;
  TilesetSelectorFrame.JsonDataType := 'application/rpgmv-tilesets';
  TilesetSelectorFrame.CreateElementCallback := @Db.CreateEmptyTileset;

  FillTilesetModeComboBox(ModeComboBox);

  TilesetGraphics := TTilesetGraphics.Create;
  TilesetTilePaletteFrame.SetType(tptEditing);
  TilesetTilePaletteFrame.UpdateCallback := nil;

  LoadTilesetData(1);
end;

procedure TDatabaseTilesetsFrame.ModeComboBoxChange(Sender: TObject);
var
  Ts: TJSONObject = nil;
begin
  if not IsLoading then
    Ts := GetSelectedTileset;
  if Ts <> nil then
    Ts.Integers['mode'] := ModeComboBox.ItemIndex;
end;

procedure TDatabaseTilesetsFrame.NameEditChange(Sender: TObject);
var
  Ts: TJSONObject = nil;
begin
  if not IsLoading then
    Ts := GetSelectedTileset;
  if Ts <> nil then begin
    Ts.Strings['name'] := NameEdit.Text;
    TilesetSelectorFrame.RefreshSelectedName;
  end;
end;

procedure TDatabaseTilesetsFrame.NotesMemoChange(Sender: TObject);
var
  Ts: TJSONObject = nil;
begin
  if not IsLoading then
    Ts := GetSelectedTileset;
  if Ts <> nil then
    Ts.Strings['note'] := NotesMemo.Text;
end;

procedure TDatabaseTilesetsFrame.FourDirectionRadioButtonChange(Sender: TObject
  );
var
  Btn: TRadioButton;
begin
  Btn := Sender as TRadioButton;
  if Btn.Checked then
    TilesetTilePaletteFrame.SetEditedData(
      TilesetFlags, etsFourDir, 0
    );
end;

procedure TDatabaseTilesetsFrame.PassabilityRadioButtonChange(Sender: TObject);
var
  Btn: TRadioButton;
begin
  Btn := Sender as TRadioButton;
  if Btn.Checked then
    TilesetTilePaletteFrame.SetEditedData(
      TilesetFlags, etsPassability, 0
    );
end;

procedure TDatabaseTilesetsFrame.SingleBitRadioButtonChange(Sender: TObject);
var
  Btn: TRadioButton;
begin
  Btn := Sender as TRadioButton;
  if Btn.Checked then
    TilesetTilePaletteFrame.SetEditedData(
      TilesetFlags, etsSingleBit, Btn.Tag
    );
end;

procedure TDatabaseTilesetsFrame.LoadTilesetData(NewId: Integer);
var
  Tileset: TJSONObject;
  TilesetName, Notes: String;
  Mode: Integer;
  TilesetNameJson, NotesJson: TJSONString;
  ModeJson: TJSONNumber;
  TilesetNamesJson: TJSONArray = nil;
begin
  IsLoading := True;
  CurrentId := NewId;

  Tileset := GetSelectedTileset;

  TilesetName := '';
  if (Tileset <> nil) and Tileset.Find('name', TilesetNameJson) then
    TilesetName := TilesetNameJson.AsString;
  NameEdit.Text := TilesetName;

  Mode := 0;
  if (Tileset <> nil) and Tileset.Find('mode', ModeJson) then
    Mode := ModeJson.AsInteger;
  ModeComboBox.ItemIndex := Mode;

  if (Tileset <> nil) then
    Tileset.Find('tilesetNames', TilesetNamesJson);
  LoadTilesetNames(TilesetNamesJson);


  Notes := '';
  if (Tileset <> nil) and Tileset.Find('note', NotesJson) then
    Notes := NotesJson.AsString;
  NotesMemo.Text := Notes;

  UpdateTilesetGraphics(Tileset);

  IsLoading := False;
end;

function TDatabaseTilesetsFrame.GetSelectedTileset: TJSONObject;
begin
  GetSelectedTileset := Db.Tilesets.Objects[CurrentId];
end;

procedure TDatabaseTilesetsFrame.TerrainTagRadioButtonChange(Sender: TObject);
var
  Btn: TRadioButton;
begin
  Btn := Sender as TRadioButton;
  if Btn.Checked then
    TilesetTilePaletteFrame.SetEditedData(
      TilesetFlags, etsTerrainTag, 0
    );
end;

procedure TDatabaseTilesetsFrame.TilesetEditButtonClick(Sender: TObject);
var
  Tileset: TJSONObject;
  TilesetIndex: Integer;
  CurrentName: String;
begin
  TilesetIndex := (Sender as TEditButton).Tag;

  Tileset := GetSelectedTileset;
  CurrentName := Tileset.Arrays['tilesetNames'].Strings[TilesetIndex];

  if ImageSelectionForm.ShowTilesetImageSelection(CurrentName) then begin
    Tileset.Arrays['tilesetNames'].Strings[TilesetIndex] :=
                                        ImageSelectionForm.SelectedFilename;
    TilesetTilePaletteFrame.Hide;
    UpdateTilesetGraphics(Tileset);
    (Sender as TEditButton).Text := ImageSelectionForm.SelectedFilename;
    TilesetTilePaletteFrame.Show;
  end;
end;

procedure TDatabaseTilesetsFrame.UpdateTilesetGraphics(Ts: TJSONObject);
var
  Ets: TEditedTileSetting;
begin
  TilesetGraphics.SetTileset(Ts);
  TilesetTilePaletteFrame.UpdateFromTileset(TilesetGraphics, Ts);
  TilesetFlags := Ts.Arrays['flags'];
  TilesetTilePaletteFrame.SetEditedData(TilesetFlags, GetEditedTileSetting, GetEditedBit);

end;

procedure TDatabaseTilesetsFrame.LoadTilesetNames(Names: TJSONArray);
  procedure ProcessEditButon(Btn: TEditButton);
  var
    Value: String = '';
  begin
    if (Names <> nil) and (Btn.Tag >= 0) and (Btn.Tag < Names.Count) then
      Value := Names.Strings[Btn.Tag];
    Btn.Text := Value;
  end;

begin
  ProcessEditButon(A1EditButton);
  ProcessEditButon(A2EditButton);
  ProcessEditButon(A3EditButton);
  ProcessEditButon(A4EditButton);
  ProcessEditButon(A5EditButton);
  ProcessEditButon(BEditButton);
  ProcessEditButon(CEditButton);
  ProcessEditButon(DEditButton);
  ProcessEditButon(EEditButton);
end;

function TDatabaseTilesetsFrame.GetEditedTileSetting: TEditedTileSetting;
begin
  if PassabilityRadioButton.Checked then
    GetEditedTileSetting := etsPassability
  else if FourDirectionRadioButton.Checked then
    GetEditedTileSetting := etsFourDir
  else if TerrainTagRadioButton.Checked then
    GetEditedTileSetting := etsTerrainTag
  else
    GetEditedTileSetting := etsSingleBit;
end;

function TDatabaseTilesetsFrame.GetEditedBit: Integer;
var
  SelectedTag: Integer = 0;

  procedure CheckRadioButton(Rb: TRadioButton);
  begin
    if Rb.Checked then
      SelectedTag := Rb.Tag;
  end;

begin
  CheckRadioButton(LadderRadioButton);
  CheckRadioButton(BushRadioButton);
  CheckRadioButton(CounterRadioButton);
  CheckRadioButton(DamageRadioButton);
  CheckRadioButton(ImpassableByBoatRadioButton);
  CheckRadioButton(ImpassableByShipRadioButton);
  CheckRadioButton(AirsipCantLandRadioButton);

  GetEditedBit := SelectedTag;
end;

destructor TDatabaseTilesetsFrame.Destroy;
begin
  if TilesetGraphics <> nil then
    TilesetGraphics.Free;

  inherited Destroy;
end;

end.

